package it.koutifaris.travianmap.communication.mapdownloader;

import it.koutifaris.travianmap.context.HasApplicationContex;
import it.koutifaris.travianmap.dal.TravianServerService;
import it.koutifaris.travianmap.dal.TravianServerService.TravianServer;
import it.koutifaris.travianmap.mapsqlparser.MapSqlEntryHandler;
import it.koutifaris.travianmap.mapsqlparser.MapSqlParser;
import it.koutifaris.travianmap.mapsqlparser.ProgressHandler;
import it.koutifaris.travianmap.mapsqlparser.SqlParserException;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.zip.GZIPInputStream;

public class DownloadMapService extends HasApplicationContex {

	private final MapSqlEntryHandler entryHandler;
	private final MapSqlParser parser;

	public DownloadMapService(MapSqlEntryHandler entryHandler, MapSqlParser parser) {
		this.entryHandler = entryHandler;
		this.parser = parser;
	}

	public void download(TravianServer server, ProgressHandler progressHandler) throws IOException, SqlParserException {
		URL url = server.getMapFileUrl();
		URLConnection connection = url.openConnection();
		connection.connect();

		progressHandler.init(connection.getContentLength());
		Date lastModified = new Date(connection.getLastModified());

		parser.parse(
				new RemoteSourceImpl(connection.getInputStream(), lastModified),
				entryHandler,
				new OnDoneWrapper(progressHandler, server.getServerId(), lastModified));

	}

	private static class RemoteSourceImpl implements MapSqlParser.RemoteSource {

		private final InputStream rawStream;
		private final Date lastModificationDate;

		public RemoteSourceImpl(InputStream stream, Date lastModificationDate) {
			this.rawStream = stream;
			this.lastModificationDate = lastModificationDate;
		}

		@Override
		public Date lastModified() {
			return lastModificationDate;
		}

		@Override
		public InputStream sourceStream(StreamWrapper streamWrapper) {
			return gzWrap(streamWrapper.wrap(rawStream));
		}

		private GZIPInputStream gzWrap(InputStream inputStream) {
			try {
				return new GZIPInputStream(inputStream);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}

	}

	private class OnDoneWrapper implements ProgressHandler {

		private final ProgressHandler delegate;
		private final String server;
		private final Date lastModificationDate;

		public OnDoneWrapper(ProgressHandler delegate, String server, Date lastModificationDate) {
			this.delegate = delegate;
			this.server = server;
			this.lastModificationDate = lastModificationDate;
		}

		@Override
		public void init(long contentLength) {
			delegate.init(contentLength);
		}

		@Override
		public void onProgress(long byteCount) {
			delegate.onProgress(byteCount);
		}

		@Override
		public void onFinish(long byteCount) {
			updateLastMapUpdate();
			delegate.onFinish(byteCount);
		}

		private void updateLastMapUpdate() {
			TravianServerService travianServerService = getTravianMapContext().getServerServiceFactory().travianServerService();
			travianServerService.updateServer(server, lastModificationDate);
			travianServerService.close();
		}
	}

}
